// 版权归 瞄你个汪 所有。使用本代码应遵守相关法律法规和Apache 2.0开源许可要求。
package top.geeke.toolkit;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import top.geeke.config.AppConfiguration;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 *  JWT 加解密类
 * </p>
 *
 * @author 瞄你个汪
 * @since 2024/6/7 14:14
 */
@Component
public class JwtUtils {
    @Autowired
    private AppConfiguration appConfiguration;

    /**
     * 生成 Token
     * @param payload 荷载
     * @param expire 过期时间(分)
     */
    public String encrypt(Map<String,Object> payload, long expire) {
        DateTime now = DateTime.now();
        payload.put(JWTPayload.ISSUED_AT, now); // 签发时间
        payload.put(JWTPayload.NOT_BEFORE, now); // 生效时间
        payload.put(JWTPayload.EXPIRES_AT, now.offsetNew(DateField.MINUTE, Math.toIntExact(expire))); // 过期时间

        String key = appConfiguration.getCryptogram().getPrivateKey();
        return JWTUtil.createToken(payload, key.getBytes());
    }

    /**
     * 生成刷新 Token
     * @param token token
     * @param expire 过期时间(分)
     */
    public String createRefreshToken(String token, long expire) {
        String[] tokenParagraphs = Arrays.stream(token.split("\\."))
                .filter(Strings::isNotEmpty).toArray(String[]::new);
        int s = RandomUtil.randomInt(10, tokenParagraphs[1].length() / 2 + 2);
        int l = s + RandomUtil.randomInt(3, 13);
        return encrypt(new HashMap<String,Object>() {{
            put("f", tokenParagraphs[0]);
            put("e", tokenParagraphs[2]);
            put("s", s);
            put("l", l);
            put("k", tokenParagraphs[1].substring(s, l));
        }}, expire);
    }

    /**
     * 通过过期Token 和 刷新Token 换取新的 Token
     * @param expiredToken 过期Token
     * @param refreshToken 刷新Token
     * @param expired 过期时间
     */
    public String exchange(String expiredToken, String refreshToken, long expired) {
        // 原Token必须已过期
        String key = appConfiguration.getCryptogram().getPrivateKey();
        if (JWTUtil.verify(expiredToken, key.getBytes())) return null;

        // 判断刷新Token 是否过期
        if (JWTUtil.verify(refreshToken, key.getBytes())) return null;

        String[] tokenParagraphs = Arrays.stream(expiredToken.split("\\."))
                .filter(Strings::isNotEmpty).toArray(String[]::new);

        // 判断各个部分是否匹配
        JWT refreshTokenObj = JWTUtil.parseToken(refreshToken);
        if (!refreshTokenObj.getPayload("f").equals(tokenParagraphs[0])) return null;
        if (!refreshTokenObj.getPayload("e").equals(tokenParagraphs[2])) return null;
        if (!tokenParagraphs[1].substring(
                Integer.parseInt(refreshTokenObj.getPayload("s").toString()),
                Integer.parseInt(refreshTokenObj.getPayload("l").toString()))
           .equals(refreshTokenObj.getPayload("k"))) return null;

        // 获取过期 Token 的存储信息
        JWT jwtSecurityToken = JWTUtil.parseToken(expiredToken);
        JSONObject payloads = jwtSecurityToken.getPayloads();

        // 仅保留用户设置的荷载
        Map<String,Object> newPayload = new HashMap<>();
        newPayload.put(JWTPayload.JWT_ID, payloads.getLong(JWTPayload.JWT_ID));
        return encrypt(newPayload, expired);
    }

    /**
     * 校验TOKEN
     */
    public boolean verify(String token) {
        String key = appConfiguration.getCryptogram().getPrivateKey();
        JWT jwt = JWTUtil.parseToken(token);
        jwt.setKey(key.getBytes());
        return jwt.verify() && jwt.validate(5);
    }

    /**
     * 从token中获取用户ID
     */
    public String getJwtIdFromToken(String token) {
        JSONObject payloads = JWTUtil.parseToken(token).getPayloads();
        return payloads == null ? null : payloads.getStr(JWTPayload.JWT_ID);
    }
}
